﻿using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Drawing;

namespace PickGold.Util.OCR
{
	/// <summary/>
	[Serializable]
	public enum MatchModelLevel : byte { Lowest = 0, Normal = 1, Highest = 2 }

	/// <summary>
	/// 图像灰度化标志
	/// </summary>
	[Flags]
	public enum GrayFlag
	{
		/// <summary>
		/// 加权值
		/// </summary>
		Weighting = 0,
		/// <summary>
		/// 最大值
		/// </summary>
		Max = 1,
		/// <summary>
		/// 最小值
		/// </summary>
		Min = 2,
		/// <summary>
		/// 平均值
		/// </summary>
		Average = 4,
		/// <summary>
		/// Alpha通道
		/// </summary>
		A = 8,
		/// <summary>
		/// 红色通道
		/// </summary>
		R = 16,
		/// <summary>
		/// 绿色通道
		/// </summary>
		G = 32,
		/// <summary>
		/// 蓝色通道
		/// </summary>
		B = 64
	}

	/// <summary/>
	public class MatchModelDictionary : Dictionary<string, MatchModel> { }
	/// <summary/>
	public class MatchModelFileDictionary : Dictionary<FileInfo, MatchModelDictionary> { }

	/// <summary/>
	public class Emf
	{
		/// <summary/>
		public Metafile Metafile;

		/// <summary/>
		public Emf() { }

		/// <summary/>
		public Emf(Metafile metafile)
		{
			this.Metafile = metafile;
		}

		/// <summary/>
		public Emf(Stream metafile)
		{
			this.Metafile = new Metafile(metafile);
		}

		/// <summary/>
		public Emf(string metafile)
		{
			this.Metafile = new Metafile(metafile);
		}

		/// <summary/>
		public bool PlayRecord(EmfPlusRecordType recordType, int flags, int dataSize, IntPtr data, PlayRecordCallback callbackData)
		{
			byte[] dataArray = null;
			if (data != IntPtr.Zero)
			{
				// Copy the unmanaged record to a managed byte buffer 
				// that can be used by PlayRecord.
				dataArray = new byte[dataSize];
				Marshal.Copy(data, dataArray, 0, dataSize);
			}
			this.Metafile.PlayRecord(recordType, flags, dataSize, dataArray);
			return true;
		}

		/// <summary/>
		public void Draw(Graphics graphics, Point dest)
		{
			graphics.EnumerateMetafile(this.Metafile, dest, this.PlayRecord);
		}

		/// <summary/>
		public void Draw(Graphics graphics, Rectangle dest)
		{
			graphics.EnumerateMetafile(this.Metafile, dest, this.PlayRecord);
		}

		/// <summary/>
		public void Draw(Graphics graphics, int x, int y)
		{
			graphics.EnumerateMetafile(this.Metafile, new Point(x, y), this.PlayRecord);
		}

		/// <summary/>
		public void Draw(Graphics graphics, int x, int y, int w, int h)
		{
			graphics.EnumerateMetafile(this.Metafile, new Rectangle(x, y, w, h), this.PlayRecord);
		}

		/// <summary/>
		public static void Draw(Graphics graphics, Metafile metafile, Point dest)
		{
			new Emf(metafile).Draw(graphics, dest);
		}

		/// <summary/>
		public static void Draw(Graphics graphics, Metafile metafile, Rectangle dest)
		{
			new Emf(metafile).Draw(graphics, dest);
		}

		/// <summary/>
		public static void Draw(Graphics graphics, Metafile metafile, int x, int y)
		{
			new Emf(metafile).Draw(graphics, x, y);
		}

		/// <summary/>
		public static void Draw(Graphics graphics, Metafile metafile, int x, int y, int w, int h)
		{
			new Emf(metafile).Draw(graphics, x, y, w, h);
		}

		/// <summary/>
		public static void Draw(Graphics graphics, Stream metafile, Point dest)
		{
			new Emf(metafile).Draw(graphics, dest);
		}

		/// <summary/>
		public static void Draw(Graphics graphics, Stream metafile, Rectangle dest)
		{
			new Emf(metafile).Draw(graphics, dest);
		}

		/// <summary/>
		public static void Draw(Graphics graphics, Stream metafile, int x, int y)
		{
			new Emf(metafile).Draw(graphics, x, y);
		}

		/// <summary/>
		public static void Draw(Graphics graphics, Stream metafile, int x, int y, int w, int h)
		{
			new Emf(metafile).Draw(graphics, x, y, w, h);
		}

		/// <summary/>
		public static void Draw(Graphics graphics, string metafile, Point dest)
		{
			new Emf(metafile).Draw(graphics, dest);
		}

		/// <summary/>
		public static void Draw(Graphics graphics, string metafile, Rectangle dest)
		{
			new Emf(metafile).Draw(graphics, dest);
		}

		/// <summary/>
		public static void Draw(Graphics graphics, string metafile, int x, int y)
		{
			new Emf(metafile).Draw(graphics, x, y);
		}

		/// <summary/>
		public static void Draw(Graphics graphics, string metafile, int x, int y, int w, int h)
		{
			new Emf(metafile).Draw(graphics, x, y, w, h);
		}

		/// <summary/>
		public static void Draw(Stream output, byte[] matrix, Brush light, Brush dark, int width)
		{
			if (matrix == null || output == null || matrix.Length <= 0 || !output.CanWrite)
				return;

			if (width <= 0)
				width = matrix.Length;
			var height = matrix.Length / width;
			if (light == null && dark == null)
				dark = new SolidBrush(Color.Black);
			using (var bmp = new Bitmap(1, 1))
			{
				using (var gs = Graphics.FromImage(bmp))
				{
					using (var mf = new Metafile(output, gs.GetHdc()))
					{
						using (var g = Graphics.FromImage(mf))
						{
							for (var h = height - 1; h >= 0; h--)
							{
								for (var w = width - 1; w >= 0; w--)
								{
									if (matrix[h * width + w] > byte.MinValue)
									{
										if (dark != null)
											g.FillRectangle(dark, w * sbyte.MaxValue, h * sbyte.MaxValue, sbyte.MaxValue, sbyte.MaxValue);
									}
									else
									{
										if (light != null)
											g.FillRectangle(light, w * sbyte.MaxValue, h * sbyte.MaxValue, sbyte.MaxValue, sbyte.MaxValue);
									}
								}
							}
							g.Save();
							g.Dispose();
						}
						mf.Dispose();
					}
					gs.Dispose();
				}
				bmp.Dispose();
			}
			output = null;
		}

		/// <summary/>
		public static void Draw(Stream output, string text, Font font, Brush brush, RectangleF dest)
		{
			if (string.IsNullOrWhiteSpace(text) || output == null || !output.CanWrite)
				return;

			if (font == null)
				font = new Font(FontFamily.GenericMonospace, sbyte.MaxValue);//8);//
			if (brush == null)
				brush = new SolidBrush(Color.Black);
			using (var bmp = new Bitmap(1, 1))
			{
				using (var gs = Graphics.FromImage(bmp))
				{
					using (var mf = new Metafile(output, gs.GetHdc()))
					{
						using (var g = Graphics.FromImage(mf))
						{
							if (dest.Width > 0F && dest.Height > 0F)
								g.DrawString(text, font, brush, dest);
							else
								g.DrawString(text, font, brush, dest.X, dest.Y);
						}
						mf.Dispose();
					}
					gs.Dispose();
				}
				bmp.Dispose();
			}
			output = null;
		}

		/// <summary/>
		public static void Draw(Stream output, string text, Font font, Brush brush, float x, float y)
		{
			Draw(output, text, font, brush, new RectangleF(x, y, 0F, 0F));
		}

		/// <summary/>
		public static void Draw(Stream output, string text, Font font, Brush brush, float x, float y, float w, float h)
		{
			Draw(output, text, font, brush, new RectangleF(x, y, w, h));
		}
	}

	/// <summary/>
	public static class GdiUtil
	{
		/// <summary>
		/// 将图像转换为索引格式
		/// </summary>
		/// <param name="input">输入图像</param>
		/// <returns>如果输入图像颜色数量未超过256色，则返回索引格式图像，否则返回空</returns>
		public static Bitmap GetIndexed(this Bitmap input)
		{
			if (input == null)
				return null;

			var bw = input.Width;
			var bh = input.Height;
			var cs = new List<Color>();//获取图像颜色数量，超过256色将无法建立调色盘
			for (var x = 0; x < bw; x++)
			{
				for (var y = 0; y < bh; y++)
				{
					var c = input.GetPixel(x, y);
					var p = cs.Count;
					if (p > byte.MaxValue)//超过最大索引格式调色盘大小
						return null;

					for (--p; p >= 0; p--)
					{
						if (cs[p] == c)
							break;
					}
					if (p < 0)
						cs.Add(c);
				}
			}
			var cl = cs.Count;
			var bi = new Bitmap(bw, bh, cl > 16 ? PixelFormat.Format8bppIndexed : cl > 2 ? PixelFormat.Format4bppIndexed : PixelFormat.Format1bppIndexed);
			var cp = bi.Palette;
			var bd = bi.LockBits(new Rectangle(0, 0, bi.Width, bi.Height), ImageLockMode.WriteOnly, bi.PixelFormat);
			switch (bi.PixelFormat)
			{
				case PixelFormat.Format1bppIndexed://当前指针所指的字节中的每一位都表示一个像素的索引颜色
					var sw = ((bw + 8 * 4 - 1) / 8) / 4 * 4;//行宽，每行字节数，对齐到4的倍数
					var bs = new byte[sw * bh];
					for (var x = 0; x < bw; x++)
					{
						for (var y = 0; y < bh; y++)
						{
							if (input.GetPixel(x, y) != cs[0])//调色盘为两色
								bs[y * sw + x / 8] = (byte)(bs[y * sw + x / 8] | (1 << (7 - x % 8)));//最左边的像素为8，最右边的像素为0
						}
					}
					Marshal.Copy(bs, 0, bd.Scan0, bs.Length);
					break;
				case PixelFormat.Format4bppIndexed://当前指针所指的字节包括两个像素，通过高位和低位索引16色调色盘
					sw = ((bw + 2 * 4 - 1) / 2) / 4 * 4;//行宽，每行字节数，对齐到4的倍数
					bs = new byte[sw * bh];
					for (var x = 0; x < bw; x++)
					{
						for (var y = 0; y < bh; y++)
						{
							var c = input.GetPixel(x, y);
							for (var p = 0; p < cl; p++)
							{
								if (c == cs[p])
								{
									bs[y * sw + x / 2] = (byte)(bs[y * sw + x / 2] | (p << ((1 - x % 2) * 4)));//高位表示左边的像素，低位表示右边的像素
									break;
								}
							}
						}
					}
					Marshal.Copy(bs, 0, bd.Scan0, bs.Length);
					break;
				case PixelFormat.Format8bppIndexed://当前指针指向图像的256色调色盘索引
					sw = (bw + 1 * 4 - 1) / 4 * 4;//行宽，每行字节数，对齐到4的倍数
					bs = new byte[sw * bh];
					for (var x = 0; x < bw; x++)
					{
						for (var y = 0; y < bh; y++)
						{
							var c = input.GetPixel(x, y);
							for (var p = 0; p < cl; p++)
							{
								if (c == cs[p])
								{
									bs[y * sw + x] = (byte)p;
									break;
								}
							}
						}
					}
					Marshal.Copy(bs, 0, bd.Scan0, bs.Length);
					break;
			}
			bi.UnlockBits(bd);
			for (var p = 0; p < cl; p++)
				cp.Entries[p] = cs[p];//填充调色盘索引色
			bi.Palette = cp;//设置调色盘索引色
			return bi;
		}
	}
}
